package com.hw.one.transpot.fileupload.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hw.one.collect.util.JsonUtil;
import com.hw.one.transpot.fileupload.constant.Constant;
import com.hw.one.transpot.fileupload.entity.ConfContent;
import com.hw.one.transpot.fileupload.entity.Shard;
import com.hw.one.transpot.fileupload.entity.ShardResult;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.*;


/**
 * @Class ShredUtil
 * @Author guoshenzhen
 * @Date 2018/4/1  21:39
 */
public class ShredUtil {

    /**
     * 上传文件路径
     */
    private static final String uploadPath = Constant.UPLOAD_FILE_PATH;
    /**
     * 临时文件路径
     */
    private static final String tempSavePath = Constant.TEMP_SAVE_PATH + File.separator;
    /**
     * 配置文件路径
     */
    private static final String confSavePath = Constant.CONF_SAVE_PATH + File.separator;
    static {
        File f = new File(confSavePath);
        if(!f.exists()){
            f.mkdirs();
        }
    }

    /**
     * 进行分片并且创建分片配置文件
     * @throws IOException
     */
    public static void createShredFile(String uploadFilePath) throws IOException {
        if(null == uploadFilePath){
            uploadFilePath = uploadPath;
        }
        final File file = new File(uploadFilePath);
        final int fileLength = (int)file.length();
        final int subSize = Constant.SUB_SIZE;
        final int count = (int) Math.ceil(fileLength / (double) subSize);
        int tempSize = 0;

        final RandomAccessFile randomFile = new RandomAccessFile(file, "rw");
        FileChannel channel = randomFile.getChannel();
        int begin = 0;
        ConfContent conf = new ConfContent();
        conf.setFileName(file.getName());
        conf.setSize(fileLength);
        conf.setSubLength(count);
        conf.setSubSize(subSize);
        List<Shard> shardList = new ArrayList<>();

        RandomAccessFile tempRandomFile = null;
        for (int i = 0; i < count; i++) {
            begin = i * subSize;
            tempSize = fileLength - begin < subSize ? (int) (fileLength - begin) : subSize;
            String subFileName = UUID.randomUUID().toString();
            File tempFile = new File(tempSavePath + subFileName);
            if(!file.exists()){
                file.createNewFile();
            }
            tempRandomFile = new RandomAccessFile(tempFile, "rw");
            channel.position(0L);
            channel.transferTo(begin, tempSize,tempRandomFile.getChannel());
            tempRandomFile.close();
            Shard shard = new Shard();
            shard.setSubFileName(subFileName);
            shard.setIndex(i);
            shard.setBegin(begin);
            shard.setSize(tempSize);
            shard.setHasComplete(false);
            shardList.add(shard);
        }
        randomFile.close();
        conf.setShard(shardList);
        String confStr = JsonUtil.jsonObj2Sting(conf);
        writeToFile(confStr);
    }

    public static final String getConfFilePath(){
        String fileName = uploadPath.substring(uploadPath.lastIndexOf(File.separator) + 1);
        return confSavePath + "" + fileName + ".conf";
    }

    public static final boolean existsConfFile(){
        File file = new File(getConfFilePath());
        return file.exists();
    }

    public static final boolean isConfExists(){
        return new File(getConfFilePath()).exists();
    }



    public static void writeToFile(String json) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(getConfFilePath()));
        writer.write(json);
        writer.flush();
        writer.close();
    }

    public static String readFromFile() throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(getConfFilePath()));
        String temp;
        StringBuilder builder = new StringBuilder();
        while(null != (temp = reader.readLine())){
            builder.append(temp);
        }
        reader.close();
        return builder.toString();
    }

    public static ConfContent getConfContent() throws IOException{
        String s  = readFromFile();
        ConfContent conf = JSON.parseObject(s, ConfContent.class);
        return conf;
    }

    public static List<Shard> getShred() throws IOException{
        ConfContent confContent = getConfContent();
        if(null != confContent){
            return confContent.getShard();
        }
        return null;
    }

    public static Shard nextUnCompleteShred() throws IOException {
        List<Shard> shardList = getShred();
        Iterator<Shard> iterator = shardList.iterator();
        while(iterator.hasNext()){
            Shard next = iterator.next();
            if(!next.getHasComplete()){
                return next;
            }
        }
        return null;
    }

    public static boolean isUnComplete() throws IOException {
        List<Shard> shardList = getShred();
        Iterator<Shard> iterator = shardList.iterator();
        boolean flag = false;
        while(iterator.hasNext()){
            Shard next = iterator.next();
            if(!next.getHasComplete()){
                flag = true;
                break;
            }
        }
        return flag;
    }
    public static boolean isComplete() throws IOException{
        return !isUnComplete();
    }

    /**
     * 合并操作
     * @return
     * @throws IOException
     */
    public static boolean combine() throws IOException {
        if(isComplete()){
            ConfContent confContent = getConfContent();
            List<Shard> shard = confContent.getShard();
            if(null != shard){
                Iterator<Shard> iterator = shard.iterator();
                Map<Integer, Shard> map = new TreeMap<>();
                while(iterator.hasNext()){
                    final Shard next = iterator.next();
                    map.put(next.getIndex() , next);
                }
                String path = tempSavePath + confContent.getFileName() + "001";
                RandomAccessFile file = new RandomAccessFile(path, "rw");
                FileChannel channel = file.getChannel();
                Collection<Shard> values = map.values();
                Iterator<Shard> valuesIterator = values.iterator();
                RandomAccessFile temp;
                while(valuesIterator.hasNext()){
                    Shard next = valuesIterator.next();
                    temp = new RandomAccessFile(tempSavePath + next.getSubFileName(), "r");
                    channel.transferFrom(temp.getChannel(), next.getBegin(),next.getSize());
                    temp.close();
                }
                file.close();
                return true;
            }
        }
        return false;
    }

    /**
     * 清楚临时文件及配置文件
     */
    public static final void clearTemp() throws IOException{
        File f = new File(getConfFilePath());
        if(f.exists()){
            ConfContent confContent = getConfContent();
            if(null != confContent){
                List<Shard> shard = confContent.getShard();
                File temp = null;
                Iterator<Shard> iterator = shard.iterator();
                while(iterator.hasNext()){
                    Shard next = iterator.next();
                    temp = new File(tempSavePath + next.getSubFileName());
                    boolean isDelate = temp.delete();
                }
                f.delete();
            }
        }
        UploadUtil.clearCompressFile();
    }


    public static final ShardResult nextShredData() throws IOException {
        if (ShredUtil.isComplete()){
            return null;
        }
        Shard shard = ShredUtil.nextUnCompleteShred();
        if(null == shard){
            return null;
        }
        int size = shard.getSize();
        ShardResult data = new ShardResult();
        data.setAppId(Constant.APPID);
        data.setAppKey(Constant.APPKEY);
        data.setIndex(shard.getIndex());
        data.setSize(size);
        data.setSubFileName(shard.getSubFileName());
        data.setMethod("shard");

        RandomAccessFile randomFile  = new RandomAccessFile(tempSavePath + shard.getSubFileName(), "r");
        byte[] bytes = new byte[size];
        randomFile.read(bytes);
        randomFile.close();
        data.setBytes(bytes);
        return data;
    }

    /**
     * 修改单个分片数据
     * @param dataStr
     * @throws IOException
     */
    public static final void uploadShardConf(String dataStr) throws IOException {
        JSONObject data = JSONObject.parseObject(dataStr);
        int index = data.getIntValue("index");
        int size = data.getIntValue("size");
        ConfContent confContent = ShredUtil.getConfContent();
        List<Shard> shard = confContent.getShard();
        Iterator<Shard> iterator = shard.iterator();
        while(iterator.hasNext()){
            Shard next = iterator.next();
            if(next.getIndex().equals(index) && next.getSize().equals(size)){
                // 修改配置文件
                next.setHasComplete(true);
                String s = JSONObject.toJSONString(confContent);
                ShredUtil.writeToFile(s);
                break;
            }
        }
    }

    /**
     * 修改缺失分片数据
     * @param datasStr
     * @throws IOException
     */
    public static final void uploadDatasConf(String datasStr) throws IOException {
        JSONArray datas = JSONArray.parseArray(datasStr);
        if(null != datas && datas.size() > 0){
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < datas.size(); i++) {
                Integer index = datas.getJSONObject(i).getInteger("index");
                if(null != index){
                    list.add(index);
                }
            }
            ConfContent confContent = ShredUtil.getConfContent();
            List<Shard> shard = confContent.getShard();
            Iterator<Shard> iterator = shard.iterator();
            while(iterator.hasNext()){
                Shard next = iterator.next();
                if(list.contains(next.getIndex())){
                    // 修改配置文件
                    next.setHasComplete(false);
                }
            }
            String s = JSONObject.toJSONString(confContent);
            ShredUtil.writeToFile(s);
        }
    }



}
